﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace Kevin.Framework.Validation
{
    internal class MetadataValidatorBuilder
    {
        public MetadataValidatorBuilder()
        {
        }

        public Validator CreateValidator(Type type, string ruleset, List<string> unValidates)
        {
            Validator validator = null; ;

            if (ValidatorCache.validatorCacheQueue.TryGetValue(type.FullName + ruleset, out validator) == false)
            {
                List<Validator> list = new List<Validator>();

                GetValidatorsFromType(type, ruleset, list);
                GetValidatorsFromProperties(type, ruleset, list, unValidates);
                GetValidatorsFromFields(type, ruleset, list, unValidates);

                validator = new AndCompositeValidator(list.ToArray());

                ValidatorCache.validatorCacheQueue.Add(type.FullName + ruleset, validator);
            }

            return validator;
        }

        #region GetValidatorsFromProperties

        private void GetValidatorsFromProperties(Type type, string ruleset, List<Validator> list, List<string> unValidates)
        {
            PropertyInfo[] pInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);

            foreach (PropertyInfo pi in pInfos)
            {
                if (unValidates != null && unValidates.IndexOf(pi.Name) > 0)
                {
                    FillValidatorsFromProperty(pi, ruleset, list);
                }
            }
        }

        private void GetValidatorsFromProperties(Type type, string ruleset, List<Validator> list)
        {
            GetValidatorsFromProperties(type, ruleset, list, null);
        }

        private void FillValidatorsFromProperty(PropertyInfo info, string ruleset, List<Validator> list)
        {
            CompositeValidatorBuilder builder = GetCompositeValidatorBuilder(info, ruleset);

            foreach (ValidatorAttribute attr in info.GetCustomAttributes(typeof(ValidatorAttribute), true))
            {
                if (attr.Ruleset == ruleset && info.CanRead)
                {
                    ValueAccessValidator valueValidator = new ValueAccessValidator(
                        new PropertyValueAccess(info),
                        attr.CreateValidator(info.PropertyType, info.ReflectedType));

                    builder.AddValueValidator(valueValidator);
                }
            }

            if (builder.GetCompositeValidatorsCount() != 0)
                list.Add(builder.GetValidator());
        }

        #endregion

        #region GetValidatorsFromFields

        private void GetValidatorsFromFields(Type type, string ruleset, List<Validator> list, List<string> unValidates)
        {
            FieldInfo[] fieldInfo = type.GetFields(BindingFlags.Instance | BindingFlags.Public);

            foreach (FieldInfo info in fieldInfo)
            {
                if (unValidates != null && unValidates.IndexOf(info.Name) < 0)
                    FillValidatorsFromField(info, ruleset, list);
            }
        }

        private void GetValidatorsFromFields(Type type, string ruleset, List<Validator> list)
        {
            GetValidatorsFromFields(type, ruleset, list, null);
        }

        private void FillValidatorsFromField(FieldInfo info, string ruleset, List<Validator> list)
        {
            CompositeValidatorBuilder builder = GetCompositeValidatorBuilder(info, ruleset);

            foreach (ValidatorAttribute attr in info.GetCustomAttributes(typeof(ValidatorAttribute), true))
            {
                if (attr.Ruleset == ruleset)
                {
                    ValueAccessValidator valueValidator = new ValueAccessValidator(
                        new FieldValueAccess(info),
                        attr.CreateValidator(info.FieldType, info.ReflectedType));

                    builder.AddValueValidator(valueValidator);
                }
            }

            if (builder.GetCompositeValidatorsCount() != 0)
                list.Add(builder.GetValidator());
        }

        #endregion

        #region GetValidatorsFromType

        private void GetValidatorsFromType(Type type, string ruleset, List<Validator> list)
        {
            CompositeValidatorBuilder builder = GetCompositeValidatorBuilder(type, ruleset);

            foreach (ValidatorAttribute attr in type.GetCustomAttributes(typeof(ValidatorAttribute), true))
            {
                if (attr.Ruleset == ruleset)
                {
                    builder.AddValueValidator(attr.CreateValidator(type, type.ReflectedType));
                }
            }

            if (builder.GetCompositeValidatorsCount() != 0)
                list.Add(builder.GetValidator());
        }

        #endregion

        private CompositeValidatorBuilder GetCompositeValidatorBuilder(MemberInfo info, string ruleset)
        {
            CompositeValidatorBuilder builder = null;

            foreach (ValidatorCompositionAttribute attr in info.GetCustomAttributes(typeof(ValidatorCompositionAttribute), true))
            {
                if (attr.Ruleset == ruleset)
                {
                    builder = new CompositeValidatorBuilder(attr.CompositionType, attr.MessageTemplate);
                    break;
                }
            }

            if (builder == null)
                builder = new CompositeValidatorBuilder(CompositionType.And);

            return builder;
        }
    }
}
